package ru.compft.web.beans.table;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import ru.compft.web.utils.FacesUtils;

import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.PhaseId;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * User: VVasiliev
 * Date: 02.08.12
 * Time: 14:04
 * Бин для постраничника.
 * Его должны наследовать все бины предсталющие таблицы
 */
public abstract class PaginationBean implements Serializable {
    private static final long serialVersionUID = 5910149531364114835L;

    private static final Logger logger = LoggerFactory.getLogger(ModulesTableBean.class);

    protected static final String PAGE_PARAM = "page"; // параметр с номером страницы в Request
    protected int MAX_INDEX_PAGE = 5; // по умолчанию в постраничнике будет отображено 5 кнопок с номерами страниц
    protected int MIN_RECORDS_PER_PAGE = 1; // по умолчанию на странице будет отображена 1 запись

    private int startRecord; // номер начальной записи
    private int endRecord;  // номер конечной записи
    private int totalRecord; // всего записей
    private int currentPage;  // текущая страница
    private int totalPage; // всего страниц
    private int recordsPerPage; // всего записей на странице

    protected List pagebleRecords;

    public int getStartRecord() {
        return startRecord;
    }

    public void setStartRecord(int startRecord) {
        this.startRecord = startRecord;
    }

    public int getEndRecord() {
        return endRecord;
    }

    public void setEndRecord(int endRecord) {
        this.endRecord = endRecord;
    }

    public int getTotalRecord() {
        return totalRecord;
    }

    public void setTotalRecord(int totalRecord) {
        this.totalRecord = totalRecord;
    }

    public int getCurrentPage() {
        return currentPage <= 1 ? 1 : currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getTotalPage() {
        return totalPage <= 1 ? 1 : totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getRecordsPerPage() {
        // Меньше чем минимальное количество записей на странице быть не должно
        return recordsPerPage < MIN_RECORDS_PER_PAGE ? MIN_RECORDS_PER_PAGE : recordsPerPage;
    }

    public void setRecordsPerPage(int recordsPerPage) {
        this.recordsPerPage = recordsPerPage;
    }

    public List getPagebleRecords() {
        if (pagebleRecords == null) setPagebleRecords(new LinkedList());
        return pagebleRecords;
    }

    public void setPagebleRecords(List pagebleRecords) {
        this.pagebleRecords = pagebleRecords;
    }

    /**
     * ссылка на первую страницу доступна
     *
     * @return
     */
    public boolean isFirstPageEnabled() {
        return totalPage > 1 && currentPage > 1;
    }

    /**
     * ссылка на предыдущую страницу доступна
     *
     * @return
     */
    public boolean isPreviousPageEnabled() {
        return isFirstPageEnabled();
    }

    /**
     * ссылка на следующую страницу доступна
     *
     * @return
     */
    public boolean isNextPageEnabled() {
        return isLastPageEnabled();
    }

    /**
     * ссылка на последнюю страницу доступна
     *
     * @return
     */
    public boolean isLastPageEnabled() {
        return totalPage > 1 && currentPage != totalPage;
    }

    /**
     * Значение первой страницы.
     * Всегда 1
     *
     * @return
     */
    public int getFirstPageValue() {
        return 1;
    }

    /**
     * Значение предыдущей страницы
     *
     * @return
     */
    public int getPreviousPageValue() {
        return getCurrentPage() > 1 ? getCurrentPage() - 1 : getFirstPageValue();
    }

    /**
     * Значение следующей страницы
     *
     * @return
     */
    public int getNextPageValue() {
        return getCurrentPage() < getTotalPage() ? getCurrentPage() + 1 : getTotalPage();
    }

    /**
     * Значение последней страницы
     *
     * @return
     */
    public int getLastPageValue() {
        return getTotalPage();
    }

    /**
     * Список номеров доступных страниц в постраничнике
     *
     * @return
     */
    public List<String> getPageNumList() {
        final List<String> resultList = new ArrayList<String>();

        // страниц меньше или равно 5. Отображаем все
        if (totalPage <= MAX_INDEX_PAGE) {
            fillNumberArray(resultList, getFirstPageValue(), getLastPageValue());
        } else {
            // страниц больше 5, делаем выборку, чтоб текущая была посредине
            final int firstSub = getCurrentPage() - 2;
            // если начальный индекс меньше 2, то он будет 1 
            int firstIndex = firstSub < 2 ? 1 : firstSub;

            // если к начальному индексу прибавить MAX_INDEX_PAGE и это будет больше чем всего страниц, то начальный придется пересчитать
            if ((firstIndex + MAX_INDEX_PAGE) > getTotalPage()) {
                final int lastSub = getCurrentPage() + 2;
                // если конечный индекс больше числа страниц, то он будет максимальным числом страниц
                int lastIndex = lastSub > getTotalPage() ? getTotalPage() : lastSub;

                fillNumberArray(resultList, lastIndex - MAX_INDEX_PAGE + 1, lastIndex);

            } else {
                // если не больше, то заполним массив
                // 1 вычитаем, т.к. при заполнении массива берется конечное значение вкючительно
                fillNumberArray(resultList, firstIndex, (firstIndex + MAX_INDEX_PAGE - 1));
            }
        }
        return resultList;
    }

    /**
     * Заполняем поля для постраничника
     *
     * @param from         - индекс первой записи
     * @param count        - записей на странице
     * @param recordsList  - список записей
     * @param totalRecords - всего записей
     */
    protected void fillPagebleParams(int from, int count, List recordsList, int totalRecords) {
        if (!CollectionUtils.isEmpty(recordsList)) {
            // записи
            setStartRecord(from + 1);
            setEndRecord(Math.min((from + count), totalRecords));
            setTotalRecord(totalRecords);
            setTotalPage(countTotalPages(totalRecords));
        } else {
            // обнуляем счетчики на странице
            setStartRecord(0);
            setEndRecord(0);
            setTotalRecord(0);
            setCurrentPage(0);
            setTotalPage(0);
        }
    }

    protected void analizeTotalPage(int totalRecords) {
        // если всего страниц меньше, чем запрошенная, то выдаем предупреждение
        final int totalPages = countTotalPages(totalRecords);
        if (totalPages < getCurrentPage()) {
            // todo сделать через файл messages
            FacesUtils.addWarnMessage(null, "Максимальное количество страниц {" + totalPages + "} меньше запрошенной {" + getCurrentPage() + "}");
        }
    }

    /**
     * Получение правильного номера страницы из параметра.
     * Валидация параметра. Проверка, чтоб он не был меньше 1 и был числом
     *
     * @param pageParam
     * @return
     */
    protected int currentPageValidNumber(String pageParam) {
        int currentPage = 1;
        try {
            currentPage = Integer.parseInt(pageParam);
            // текущая страница не может быть меньше 1
            if (currentPage < 1) {
                currentPage = 1;
                // todo сделать через файл messages
                FacesUtils.addErrorMessage(null, "Текущая страница {" + pageParam + "} не может быть меньше 1");
            }
        } catch (NumberFormatException e) {
            // пишем ошибку в лог. При этом номер страницы будет 1
            logger.error("Page param = " + pageParam, e);
            // todo сделать через файл messages
            if (pageParam != null)
                FacesUtils.addErrorMessage(null, "Текущая страница {" + pageParam + "} должна быть числом");
        }
        return currentPage;
    }

    /**
     * Считаем общее количество страниц по всем записям в БД
     *
     * @param totalRecords
     * @return
     */
    private int countTotalPages(double totalRecords) {
        // страницы
        final Double ceil = Math.ceil(totalRecords / getRecordsPerPage());
        return ceil.intValue();
    }

    private void fillNumberArray(List<String> resultList, int from, int to) {
        for (int i = from; i <= to; i++) {
            resultList.add(String.valueOf(i));
        }
    }

    public void renderTable(ComponentSystemEvent event) {
        final PhaseId phaseId = FacesUtils.getCurrentPhaseId();
        System.out.println("renderTable 1= " + phaseId);
    }
    /**
     * Создаем таблицу перед созданием страницы для ее отображения, чтоб можно было подсчитать
     * номер текущей страницы
     * <p/>
     * //     * @param event
     */
//    public void renderTable(ComponentSystemEvent event) {
    public void renderTable() {
        final PhaseId phaseId = FacesUtils.getCurrentPhaseId();
        System.out.println("renderTable 2= " + phaseId);
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering caches table");
        }

        // получаем записи в таблице
//        setPagebleRecords(getRecords());
    }

    /**
     * Получаем записи для таблицы
     *
     * @return
     */
    public abstract List getRecords();
}
